/*
 * cudaUtils.cpp
 *
 *  Created on: Mar 17, 2010
 *      Author: lukasz
 */

#include "cudaUtils.h"
#include <cuda.h>
#include <cuda.h>

#include <cuda_runtime_api.h>
#include <algorithm>
#include <iostream>
#include <sstream>

void CUDA_CHECK(cudaError_t err)
{
	if (err != cudaSuccess)
	{

		std::stringstream errMsg;
		errMsg <<  "Cuda err " << cudaGetErrorString(err) << std::endl;
		throw CUDAException(errMsg.str());
	}
}

void printLastError()
{
	cudaError_t err = cudaGetLastError();
	if (err != cudaSuccess)
		std::cout << "Cuda err " << cudaGetErrorString(err) << std::endl;
}

CudaMemoryManager::CudaMemoryManager(){}

CudaMemoryManager::~CudaMemoryManager()
{
    std::for_each(d_memory.begin(), d_memory.end(), cudaFree);     
}

byte* CudaMemoryManager::malloc(uint32_t size, bool pageLocked)
{
    byte* d_in;
    if (!pageLocked)
    {
        CUDA_CHECK(cudaMalloc((void**) &d_in, size));
        d_memory.insert(d_in);
    }
    else
    {
        CUDA_CHECK( cudaMallocHost((void**)&d_in, size) );
        d_hostMemory.insert(d_in);
    }
    
    
    return d_in;
}

void CudaMemoryManager::free(byte* mem)
{	
	
	std::set<byte*>::const_iterator cit = d_memory.find(mem);
	if (cit != d_memory.end())
	{
		CUDA_CHECK(cudaFree((void*)mem));
		d_memory.erase(cit);
	}
}

void CudaMemoryManager::malloc(uint32_t size, byte** ptr)
{

    CUDA_CHECK( cudaMallocHost((void**)&ptr, size) );
    d_memory.insert(*ptr);
    
}

void CudaMemoryManager::copy(byte* dst, byte* src, uint32_t size)
{
    bool dstH = d_memory.find(dst) == d_memory.end();
    bool srcH = d_memory.find(src) == d_memory.end();
    cudaMemcpyKind kind; 
    if (!dstH && srcH)
    {
        kind = cudaMemcpyHostToDevice;
    }
    else if (!dstH && !srcH)
    {
        kind = cudaMemcpyDeviceToDevice;
    }
    else if (dstH && srcH)
    {
        kind = cudaMemcpyHostToHost;
    }
    else 
    {
        kind = cudaMemcpyDeviceToHost;
    }
    
    CUDA_CHECK(cudaMemcpy(dst, src, size, kind));
}

__global__ void KwriteToLocked(byte* mem, byte* out)
{
    out[0] = mem[0];
}

void writeToLockedMemory(byte* mem, byte* out)
{
    KwriteToLocked<<<1,1>>>(mem, out);
    
}

